//
//  job_robin.h
//  Job_Scheduling
//
//  Created by zhangyukun on 2021/4/21.
//

#ifndef job_robin_h
#define job_robin_h

#include "sort.h"
/*
This head file is to analogue the function of different type of scheduling .
**/

#define TIME_SLICE 5
#define INTR 0
#define UNINTR 1

static int count;                               //to count the run time slice .
static int now_time = 0;                      //to record the time now we are.
static double average_wait_time;                //to record the average wait time .
static double average_turnaround_time;          //to record the average turnaround time .
static double average_weighted_turnaround_time; //to record the average weighted turnaround time .

void Print(struct JobControlBlock *elem);
void FCFS(struct list_head *wait_head,struct list_head *ready_head);
void SJF(struct list_head *wait_head,struct list_head *ready_head);
void HPF(struct list_head *wait_head,struct list_head *ready_head);
void HRRN(struct list_head *wait_head,struct list_head *ready_head);
void RR(struct list_head *wait_head,struct list_head *ready_head);
void MFQ(struct list_head *wait_head,struct list_head **ready_head);
void Run(struct list_head *ready_head,struct list_head *wait_head,int type,int standard);
void Run_For_MFQ(struct list_head *wait_head,struct list_head **ready_head,int curpos,int time_count);
void Test(struct list_head *wait_head,struct list_head *ready_head);
void Init(void);
int min(int a,int b);

//print certain information of JCB when it is completed .
void Print(struct JobControlBlock *elem){
        printf("执行完的作业是：%d号作业，等待时间为：%2d\t周转时间为：%3d\t带权周转时间为:%.2f\n",elem->number,elem->wait_time,elem->turnaround_time,elem->weighted_turnaround_time);
}
//FCFS scheduling .
void FCFS(struct list_head *wait_head,struct list_head *ready_head){
    init_JCB(wait_head);                            //initialize waiting list .
    Init();                                         //initialize the global variable .
    sort(wait_head, FCFS_standard);                 //sort the waiting list with FCFS standard .
    Test(wait_head, ready_head);                    //add JCB to ready list when it comes .
    Run(ready_head, wait_head, UNINTR,FCFS_standard);//Run .
}
//SJF scheduling .
void SJF(struct list_head *wait_head,struct list_head *ready_head){
    init_JCB(wait_head);                            //initialize waiting list .
    Init();                                         //initialize the global variable .
    sort(wait_head, FCFS_standard);                 //sort the waiting list with FCFS standard .
    Test(wait_head, ready_head);                    //add JCB to ready list when it comes .
    Run(ready_head, wait_head, UNINTR,SJF_standard);//Run .
}
//HPF scheduling .
void HPF(struct list_head *wait_head,struct list_head *ready_head){
    init_JCB(wait_head);                            //initialize waiting list .
    Init();                                         //initialize the global variable .
    sort(wait_head, FCFS_standard);                 //sort the waiting list with FCFS standard .
    Test(wait_head, ready_head);                    //add JCB to ready list when it comes .
    Run(ready_head, wait_head, UNINTR,HPF_standard);//Run .
}
//HRRN scheduling .
void HRRN(struct list_head *wait_head,struct list_head *ready_head){
    init_JCB(wait_head);                            //initialize waiting list .
    Init();                                         //initialize the global variable .
    sort(wait_head, FCFS_standard);                 //sort the waiting list with FCFS standard .
    Test(wait_head, ready_head);                    //add JCB to ready list when it comes .
    Run(ready_head, wait_head, UNINTR,HRRN_standard);//Run .
}
//RR scheduling .
void RR(struct list_head *wait_head,struct list_head *ready_head){
    init_JCB(wait_head);                            //initialize waiting list .
    Init();                                         //initialize the global variable .
    sort(wait_head, FCFS_standard);                 //sort the waiting list with FCFS standard .
    Test(wait_head, ready_head);                    //add JCB to ready list when it comes .
    Run(ready_head, wait_head, INTR,FCFS_standard);//Run .
}
//schedule MFQ and run it .
void MFQ(struct list_head *wait_head,struct list_head **ready_head){
    init_JCB(wait_head);                            //initialize waiting list .
    Init();                                         //initialize the global variable .
    sort(wait_head, FCFS_standard);                 //sort the waiting list with FCFS standard .
    Test(wait_head, ready_head[0]);                    //add JCB to ready list when it comes .
    while(count){
        //if there is no JCB in ready list , do nothing and time goes by .
        if(is_empty(ready_head[0]) && is_empty(ready_head[1]) && is_empty(ready_head[2])){
            now_time += TIME_SLICE;
            Test(wait_head, ready_head[0]);
            continue;
        }
        //suppose we have three ready list .
L:
        Run_For_MFQ(wait_head, ready_head, 0, 1);
        Run_For_MFQ(wait_head, ready_head, 1, 2);
        if(!is_empty(ready_head[0])){
            goto L;
        }
        Run_For_MFQ(wait_head, ready_head, 2, 4);
        if(!is_empty(ready_head[0]) || !is_empty(ready_head[1])){
            goto L;
        }
    }
    average_wait_time /= length;
    average_turnaround_time /= length;
    average_weighted_turnaround_time /= length;
    printf("平均等待时间：%.2f\n平均周转时间：%.2f\n平均带权周转时间：%.2f\n",average_wait_time,average_turnaround_time,average_weighted_turnaround_time);
}
//schedule JCB and run it .
void Run(struct list_head *ready_head,struct list_head *wait_head,int type,int standard){
    struct list_head *curr;
    struct list_head *ocurr;
    struct JobControlBlock *ptr;
    struct JobControlBlock *optr;
    int run_count = 2;
    //continue until all JCBs are completed .
    while(count){
        //if there is no JCB in ready list , do nothing and time goes by .
        if(is_empty(ready_head)){
            now_time += TIME_SLICE;
            Test(wait_head, ready_head);
            continue;
        }
        //deal with the first JCB in ready list .
        curr = ready_head->next;
        ptr = list_entry(curr, struct JobControlBlock, list);
        //delete it from the ready list and run it .
        del(curr);
        //if it has not been dealt with before , set its start time with current time .
        if(ptr->visited == 0){
            ptr->start_time = now_time;
            ptr->visited++;
        }
        //type = 1,it is used for FCFS,SJF,HPF,HRRN since it is not interruptible .
        //type = 0,it is used for RR since it is interruptible .
        if(type){
            //run until this JCB is completed .
            while(ptr->run_time < ptr->need_time){
                ptr->run_time += TIME_SLICE;
                now_time += TIME_SLICE;
                ptr->turnaround_time  = ptr->run_time + ptr->wait_time;
                ptr->weighted_turnaround_time = (double)ptr->turnaround_time / ptr->run_time;
                //when it is running , other JCBs in ready list is waiting .
                list_for_each(ocurr, ready_head){
                    optr = list_entry(ocurr, struct JobControlBlock, list);
                    optr->wait_time += TIME_SLICE;  //its waiting time increases by TIME SLICE .
                    //change its response ratio .
                    optr->response_ratio = 1 + (double)optr->wait_time / optr->need_time;
                }
                //test whether JCB in wait list can be added to ready list .
                Test(wait_head, ready_head);
                //sort ready list with different standard .
                switch (standard) {
                    case FCFS_standard:
                        sort(ready_head, FCFS_standard);
                        break;
                    case SJF_standard:
                        sort(ready_head,SJF_standard);
                        break;
                    case HPF_standard:
                        sort(ready_head,HPF_standard);
                        break;
                    case HRRN_standard:
                        sort(ready_head,HRRN_standard);
                    default:
                        break;
                }
            }
            //if it is completed , record certatin informatin .
            if(ptr->run_time == ptr->need_time){
                count--;
                Print(ptr);
                average_wait_time += ptr->wait_time;
                average_turnaround_time += ptr->turnaround_time;
                average_weighted_turnaround_time += ptr->weighted_turnaround_time;
            }
        }
        else {
            run_count = 2;
            //run until it is completed or its time slice is over .
            while(ptr->run_time < ptr->need_time && run_count){
                ptr->run_time += TIME_SLICE;
                now_time += TIME_SLICE;
                //when it is running , other JCBs in ready list is waiting .
                list_for_each(ocurr, ready_head){
                    optr = list_entry(ocurr, struct JobControlBlock, list);
                    optr->wait_time += TIME_SLICE;  //its waiting time increases by TIME SLICE .
                }
                ptr->turnaround_time  = ptr->run_time + ptr->wait_time;
                ptr->weighted_turnaround_time = (double)ptr->turnaround_time / ptr->run_time;
                //test whether JCB in wait list can be added to ready list .
                Test(wait_head, ready_head);
                run_count--;
            }
            printf("%d号作业执行了%d\n",ptr->number, (2 - run_count) * TIME_SLICE);
            //if it is completed , record certain information .
            if(ptr->run_time == ptr->need_time){
                count--;
                Print(ptr);
                average_wait_time += ptr->wait_time;
                average_turnaround_time += ptr->turnaround_time;
                average_weighted_turnaround_time += ptr->weighted_turnaround_time;
            }
            //if it is not completed , add it to the tail of ready list .
            else {
                add(ready_head->prev, curr);
            }
        }
    }
    average_wait_time /= length;
    average_turnaround_time /= length;
    average_weighted_turnaround_time /= length;
    printf("平均等待时间：%.2f\n平均周转时间：%.2f\n平均带权周转时间：%.2f\n",average_wait_time,average_turnaround_time,average_weighted_turnaround_time);
}
//it is written to realize MFQ scheduling .
void Run_For_MFQ(struct list_head *wait_head,struct list_head **ready_head,int curpos,int time_count){
    struct list_head *curr;
    struct list_head *ocurr;
    struct JobControlBlock *ptr;
    struct JobControlBlock *optr;
    int run_count;
    while(!is_empty(ready_head[curpos])){
        //deal with the first JCB in ready list .
        curr = ready_head[curpos]->next;
        ptr = list_entry(curr, struct JobControlBlock, list);
        //delete it from the ready list and run it .
        del(curr);
        //if it has not been dealt with before , set its start time with current time .
        if(ptr->visited == 0){
            ptr->start_time = now_time;
            ptr->visited++;
        }
        run_count = time_count;
        //run until it is completed or its time slice is over .
        while(ptr->run_time < ptr->need_time && run_count){
            ptr->run_time += TIME_SLICE;
            now_time += TIME_SLICE;
            //when it is running , other JCBs in ready list is waiting .
            for(int i = 0;i < 3;i++){
                list_for_each(ocurr, ready_head[i]){
                    optr = list_entry(ocurr, struct JobControlBlock, list);
                    optr->wait_time += TIME_SLICE;  //its waiting time increases by TIME SLICE .
                }
            }
            ptr->turnaround_time  = ptr->run_time + ptr->wait_time;
            ptr->weighted_turnaround_time = (double)ptr->turnaround_time / ptr->run_time;
            //test whether JCB in wait list can be added to ready list .
            Test(wait_head, ready_head[0]);
            run_count--;
        }
        printf("%d号作业执行了%d\n",ptr->number, (time_count - run_count) * TIME_SLICE);
        //if it is completed , record certain information .
        if(ptr->run_time == ptr->need_time){
            count--;
            Print(ptr);
            average_wait_time += ptr->wait_time;
            average_turnaround_time += ptr->turnaround_time;
            average_weighted_turnaround_time += ptr->weighted_turnaround_time;
        }
        //if it is not completed , add it to the tail of ready list .
        else {
            add(ready_head[min(curpos + 1, 2)]->prev, curr);
        }
        //if the ready list with higher privilege is not empty , go back to it .
        for(int i = 1;i <= curpos;i++){
            if(!is_empty(ready_head[i - 1])){
                return;
            }
        }
    }
}
//test whether the JCB reaches now or not , if true add it to ready list .
void Test(struct list_head *wait_head,struct list_head *ready_head){
    struct list_head *curr;
    struct JobControlBlock *ptr;
    list_for_each(curr,wait_head){
        ptr = list_entry(curr, struct JobControlBlock, list);
        //if JCB reaches , add it to ready list .
        if((now_time >= ptr->reach_time) && (!ptr->isreached)){
            ptr->isreached = true;
            struct JobControlBlock *temp = malloc(sizeof(struct JobControlBlock));
            //copy its information and add it to ready list .
            memcpy(temp, ptr, sizeof(struct JobControlBlock));
            INIT_LIST_HEAD(&(temp->list));
            //add it to the tail of ready list .
            add(ready_head->prev, &(temp->list));
        }
    }
}
//initialize the global variables with initial value .
void Init(void){
    count = length;
    now_time = 0;
    average_wait_time = 0;
    average_turnaround_time = 0;
    average_weighted_turnaround_time = 0;
}
//return the number which is tinier .
int min(int a,int b){
    if(a > b){
        return b;
    }
    else {
        return a;
    }
}
#endif /* job_robin_h */
